package com.tianhai.db;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 事务管理器
 * 集成WAL预写日志和LockManager锁管理
 * 支持读已提交（Read Committed）隔离级别
 */
public class TransactionManager {
    
    private final WAL wal;
    private final LockManager lockManager;
    private final AtomicLong nextTransactionId;
    
    // 活跃事务表：<事务ID, 事务信息>
    private final Map<Long, Transaction> activeTransactions;
    
    // 事务状态
    public enum TransactionStatus {
        ACTIVE,      // 活跃
        COMMITTED,   // 已提交
        ABORTED      // 已中止
    }
    
    /**
     * 构造函数
     */
    public TransactionManager(String walFilePath) throws IOException {
        this.wal = new WAL(walFilePath);
        this.lockManager = new LockManager(5000); // 5秒锁超时
        this.nextTransactionId = new AtomicLong(1);
        this.activeTransactions = new ConcurrentHashMap<>();
    }
    
    /**
     * 开始事务
     */
    public long beginTransaction() throws IOException {
        long txnId = nextTransactionId.getAndIncrement();
        
        // 记录BEGIN日志
        wal.logTransaction(txnId, WAL.LogType.BEGIN, null, null);
        
        // 创建事务对象
        Transaction txn = new Transaction(txnId);
        activeTransactions.put(txnId, txn);
        
        return txnId;
    }
    
    /**
     * 提交事务
     */
    public void commitTransaction(long txnId) throws IOException {
        Transaction txn = activeTransactions.get(txnId);
        if (txn == null) {
            throw new IllegalStateException("事务不存在: " + txnId);
        }
        
        if (txn.status != TransactionStatus.ACTIVE) {
            throw new IllegalStateException("事务状态异常: " + txn.status);
        }
        
        try {
            // 记录COMMIT日志
            wal.logTransaction(txnId, WAL.LogType.COMMIT, null, null);
            
            // 更新事务状态
            txn.status = TransactionStatus.COMMITTED;
            
        } finally {
            // 释放所有锁（两阶段锁的收缩阶段）
            lockManager.releaseAllLocks(txnId);
            
            // 从活跃事务表移除
            activeTransactions.remove(txnId);
        }
    }
    
    /**
     * 回滚事务
     */
    public void rollbackTransaction(long txnId) throws IOException {
        Transaction txn = activeTransactions.get(txnId);
        if (txn == null) {
            throw new IllegalStateException("事务不存在: " + txnId);
        }
        
        try {
            // 记录ABORT日志
            wal.logTransaction(txnId, WAL.LogType.ABORT, null, null);
            
            // 更新事务状态
            txn.status = TransactionStatus.ABORTED;
            
        } finally {
            // 释放所有锁
            lockManager.releaseAllLocks(txnId);
            
            // 从活跃事务表移除
            activeTransactions.remove(txnId);
        }
    }
    
    /**
     * 执行INSERT操作
     * 自动获取排他锁并记录日志
     */
    public void executeInsert(long txnId, String tableName, String data) 
            throws IOException, InterruptedException {
        
        Transaction txn = validateTransaction(txnId);
        
        // 获取表的排他锁（写操作）
        boolean lockAcquired = lockManager.acquireLock(
            txnId, tableName, LockManager.LockType.EXCLUSIVE
        );
        
        if (!lockAcquired) {
            throw new RuntimeException("获取锁超时，可能发生死锁: " + tableName);
        }
        
        try {
            // 记录INSERT日志
            wal.logTransaction(txnId, WAL.LogType.INSERT, tableName, data);
            
            // 记录操作
            txn.addOperation(new Operation(WAL.LogType.INSERT, tableName, data));
            
        } catch (Exception e) {
            // 操作失败，回滚事务
            rollbackTransaction(txnId);
            throw e;
        }
    }
    
    /**
     * 执行UPDATE操作
     * 自动获取排他锁并记录日志
     */
    public void executeUpdate(long txnId, String tableName, String data) 
            throws IOException, InterruptedException {
        
        Transaction txn = validateTransaction(txnId);
        
        // 获取表的排他锁（写操作）
        boolean lockAcquired = lockManager.acquireLock(
            txnId, tableName, LockManager.LockType.EXCLUSIVE
        );
        
        if (!lockAcquired) {
            throw new RuntimeException("获取锁超时，可能发生死锁: " + tableName);
        }
        
        try {
            // 记录UPDATE日志
            wal.logTransaction(txnId, WAL.LogType.UPDATE, tableName, data);
            
            // 记录操作
            txn.addOperation(new Operation(WAL.LogType.UPDATE, tableName, data));
            
        } catch (Exception e) {
            // 操作失败，回滚事务
            rollbackTransaction(txnId);
            throw e;
        }
    }
    
    /**
     * 执行DELETE操作
     * 自动获取排他锁并记录日志
     */
    public void executeDelete(long txnId, String tableName, String data) 
            throws IOException, InterruptedException {
        
        Transaction txn = validateTransaction(txnId);
        
        // 获取表的排他锁（写操作）
        boolean lockAcquired = lockManager.acquireLock(
            txnId, tableName, LockManager.LockType.EXCLUSIVE
        );
        
        if (!lockAcquired) {
            throw new RuntimeException("获取锁超时，可能发生死锁: " + tableName);
        }
        
        try {
            // 记录DELETE日志
            wal.logTransaction(txnId, WAL.LogType.DELETE, tableName, data);
            
            // 记录操作
            txn.addOperation(new Operation(WAL.LogType.DELETE, tableName, data));
            
        } catch (Exception e) {
            // 操作失败，回滚事务
            rollbackTransaction(txnId);
            throw e;
        }
    }
    
    /**
     * 执行SELECT操作（读已提交隔离级别）
     * 读操作不需要持有锁直到事务结束（读已提交）
     */
    public void executeSelect(long txnId, String tableName) 
            throws InterruptedException {
        
        validateTransaction(txnId);
        
        // 获取表的共享锁（读操作）
        boolean lockAcquired = lockManager.acquireLock(
            txnId, tableName, LockManager.LockType.SHARED
        );
        
        if (!lockAcquired) {
            throw new RuntimeException("获取锁超时: " + tableName);
        }
        
        // 读已提交：读取完成后立即释放共享锁
        // 不需要持有锁直到事务结束
        lockManager.releaseLock(txnId, tableName);
    }
    
    /**
     * 崩溃恢复
     * 从WAL日志中恢复已提交的事务
     */
    public List<WAL.Transaction> recover() throws IOException {
        return wal.recover();
    }
    
    /**
     * 验证事务状态
     */
    private Transaction validateTransaction(long txnId) {
        Transaction txn = activeTransactions.get(txnId);
        if (txn == null) {
            throw new IllegalStateException("事务不存在: " + txnId);
        }
        if (txn.status != TransactionStatus.ACTIVE) {
            throw new IllegalStateException("事务状态异常: " + txn.status);
        }
        return txn;
    }
    
    /**
     * 获取活跃事务数量
     */
    public int getActiveTransactionCount() {
        return activeTransactions.size();
    }
    
    /**
     * 检查事务是否存在
     */
    public boolean isTransactionActive(long txnId) {
        Transaction txn = activeTransactions.get(txnId);
        return txn != null && txn.status == TransactionStatus.ACTIVE;
    }
    
    /**
     * 清空WAL日志
     */
    public void truncateWAL() throws IOException {
        wal.truncate();
    }
    
    /**
     * 关闭事务管理器
     */
    public void close() throws IOException {
        wal.close();
    }
    
    /**
     * 事务
     */
    private static class Transaction {
        final long txnId;
        TransactionStatus status;
        final List<Operation> operations;
        final long startTime;
        
        Transaction(long txnId) {
            this.txnId = txnId;
            this.status = TransactionStatus.ACTIVE;
            this.operations = new ArrayList<>();
            this.startTime = System.currentTimeMillis();
        }
        
        void addOperation(Operation op) {
            operations.add(op);
        }
        
        @Override
        public String toString() {
            return "Transaction{txnId=" + txnId + ", status=" + status + 
                   ", ops=" + operations.size() + "}";
        }
    }
    
    /**
     * 操作
     */
    private static class Operation {
        final WAL.LogType type;
        final String tableName;
        final String data;
        
        Operation(WAL.LogType type, String tableName, String data) {
            this.type = type;
            this.tableName = tableName;
            this.data = data;
        }
        
        @Override
        public String toString() {
            return type + " on " + tableName;
        }
    }
}
